package org.dromara.common.excel.core

import cn.hutool.core.collection.CollUtil
import cn.hutool.core.lang.Filter
import cn.hutool.core.util.ReflectUtil
import cn.hutool.core.util.StrUtil
import cn.idev.excel.annotation.ExcelIgnore
import cn.idev.excel.annotation.ExcelIgnoreUnannotated
import cn.idev.excel.annotation.ExcelProperty
import lombok.SneakyThrows
import org.apache.poi.ss.util.CellRangeAddress
import org.dromara.common.core.utils.reflect.ReflectUtils.getFields
import org.dromara.common.core.utils.reflect.ReflectUtils.invokeGetter
import org.dromara.common.excel.annotation.CellMerge
import java.lang.reflect.Field
import kotlin.math.max

/**
 * 单元格合并处理器
 *
 * @author LikeYouDo
 */
open class CellMergeHandler(
    private val hasTitle: Boolean = true
) {

    private var rowIndex = 0

    init {
        // 行合并开始下标
        this.rowIndex = if (hasTitle) 1 else 0
    }

    @SneakyThrows
    fun handle(rows: List<*>): MutableList<CellRangeAddress> {
        // 如果入参为空集合则返回空集
        if (CollUtil.isEmpty(rows)) {
            return mutableListOf()
        }

        // 获取有合并注解的字段
        val mergeFields = getFieldColumnIndexMap(rows[0]!!.javaClass)
        // 如果没有需要合并的字段则返回空集
        if (CollUtil.isEmpty(mergeFields)) {
            return mutableListOf()
        }

        // 结果集
        val result: MutableList<CellRangeAddress> = ArrayList()

        // 生成两两合并单元格
        val rowRepeatCellMap: MutableMap<Field, RepeatCell> = HashMap()
        for (item in mergeFields.entries) {
            val field: Field = item.key
            val itemValue: FieldColumnIndex = item.value!!
            val colNum = itemValue.colIndex
            val cellMerge = itemValue.cellMerge

            for (i in rows.indices) {
                // 当前行数据
                val currentRowObj: Any = rows[i]!!
                // 当前行数据字段值
                val currentRowObjFieldVal = invokeGetter<Any?>(currentRowObj, field.name)

                // 空值跳过不处理
                if (currentRowObjFieldVal == null || "" == currentRowObjFieldVal) {
                    continue
                }

                // 单元格合并Map是否存在数据，如果不存在则添加当前行的字段值
                if (!rowRepeatCellMap.containsKey(field)) {
                    rowRepeatCellMap[field] = RepeatCell.of(currentRowObjFieldVal, i)
                    continue
                }

                // 获取 单元格合并Map 中字段值
                val repeatCell: RepeatCell = rowRepeatCellMap[field]!!
                val cellValue = repeatCell.value
                val current = repeatCell.current

                // 检查是否满足合并条件
                // currentRowObj 当前行数据
                // rows.get(i - 1) 上一行数据 注：由于 if (!rowRepeatCellMap.containsKey(field)) 条件的存在，所以该 i 必不可能小于1
                // cellMerge 当前行字段合并注解
                val merge = isMerge(currentRowObj, rows[i - 1], cellMerge)

                // 是否添加到结果集
                var isAddResult = false
                // 最新行
                var lastRow = i + rowIndex - 1

                // 如果当前行字段值和缓存中的字段值不相等，或不满足合并条件，则替换
                if (currentRowObjFieldVal != cellValue || !merge) {
                    rowRepeatCellMap[field] = RepeatCell.of(currentRowObjFieldVal, i)
                    isAddResult = true
                }

                // 如果最后一行不能合并，检查之前的数据是否需要合并；如果最后一行可以合并，则直接合并到最后
                if (i == rows.size - 1) {
                    isAddResult = true
                    if (i > current) {
                        lastRow = i + rowIndex
                    }
                }

                if (isAddResult && i > current) {
                    result.add(CellRangeAddress(current + rowIndex, lastRow, colNum, colNum))
                }
            }
        }
        return result
    }

    /**
     * 获取带有合并注解的字段列索引和合并注解信息Map集
     */
    private fun getFieldColumnIndexMap(clazz: Class<*>): MutableMap<Field, FieldColumnIndex?> {
        val annotationPresent = clazz.isAnnotationPresent(ExcelIgnoreUnannotated::class.java)
        val fields: Array<out Field> = getFields(clazz) { field: Field ->
            if ("serialVersionUID" == field.name) {
                return@getFields false
            }
            if (field.isAnnotationPresent(ExcelIgnore::class.java)) {
                return@getFields false
            }
            return@getFields !annotationPresent || field.isAnnotationPresent(ExcelProperty::class.java)
        }

        // 有注解的字段
        val mergeFields: MutableMap<Field, FieldColumnIndex?> = HashMap()
        for (i in fields.indices) {
            val field = fields[i]
            if (!field.isAnnotationPresent(CellMerge::class.java)) {
                continue
            }
            val cm = field.getAnnotation(CellMerge::class.java)
            val index = if (cm.index == -1) i else cm.index
            mergeFields[field] = FieldColumnIndex.of(index, cm)

            if (hasTitle) {
                val property = field.getAnnotation(ExcelProperty::class.java)
                rowIndex = max(rowIndex, property.value.size)
            }
        }
        return mergeFields
    }

    private fun isMerge(currentRow: Any?, preRow: Any?, cellMerge: CellMerge): Boolean {
        val mergeBy: Array<String> = cellMerge.mergeBy
        if (StrUtil.isAllNotBlank(*mergeBy)) {
            // 比对当前行和上一行的各个属性值一一比对 如果全为真 则为真
            for (fieldName in mergeBy) {
                val valCurrent = ReflectUtil.getFieldValue(currentRow, fieldName)
                val valPre = ReflectUtil.getFieldValue(preRow, fieldName)
                if (valPre != valCurrent) {
                    // 依赖字段如有任一不等值,则标记为不可合并
                    return false
                }
            }
        }
        return true
    }

    /**
     * 单元格合并
     */
    data class RepeatCell(val value: Any?, val current: Int) {
        companion object {
            fun of(value: Any?, current: Int): RepeatCell {
                return RepeatCell(value, current)
            }
        }
    }

    /**
     * 字段列索引和合并注解信息
     */
    data class FieldColumnIndex(val colIndex: Int, val cellMerge: CellMerge) {
        companion object {
            fun of(colIndex: Int, cellMerge: CellMerge): FieldColumnIndex {
                return FieldColumnIndex(colIndex, cellMerge)
            }
        }
    }

    companion object {
        /**
         * 创建一个单元格合并处理器实例
         *
         * @param hasTitle 是否合并标题
         * @return 单元格合并处理器
         */
        @JvmStatic
        fun of(hasTitle: Boolean): CellMergeHandler {
            return CellMergeHandler(hasTitle)
        }

        /**
         * 创建一个单元格合并处理器实例（默认不合并标题）
         *
         * @return 单元格合并处理器
         */
        @JvmStatic
        fun of(): CellMergeHandler {
            return CellMergeHandler(false)
        }

    }

}
